#include "qmosquitto.h"

#include <QtDebug>

QMosquitto::QMosquitto(QObject *parent) :
    QObject(parent),
    mosquittopp(NULL, true),
    m_timer(0),
    m_hostname("127.0.0.1"),
    m_port(1883),
    m_keepalive(60),
    m_cleanSession(true),
    m_isConnected(false),
    m_clientId(),
    m_tlsEnabled(false),
    m_tls_insecure(false)
{
    //使用tls或ssl协议时，必须调用下面函数
    mosqpp::lib_init();
}

QMosquitto::~QMosquitto()
{
    m_topics.clear();
}

void QMosquitto::addTopicMatch(const QString topic, int topic_d)
{
    m_topics.insert(topic.trimmed(), topic_d);
}

int QMosquitto::removeTopicMatch(const QString topic)
{
    return m_topics.remove(topic);
}

bool QMosquitto::connectSocketNotifiers()
{
    int s=socket();
    if (s==-1) {
        qWarning() << "Failed to get mosquitto connection socket";
        return false;
    }

    m_notifier_read = new QSocketNotifier(s, QSocketNotifier::Read, this);
    QObject::connect(m_notifier_read, SIGNAL(activated(int)), this, SLOT(loopRead()));

    m_notifier_write = new QSocketNotifier(s, QSocketNotifier::Write, this);
    QObject::connect(m_notifier_write, SIGNAL(activated(int)), this, SLOT(loopWrite()));

    return true;
}

int QMosquitto::connectToHost()
{
    int r;

    if (m_isConnected)
        return MOSQ_ERR_CONN_PENDING;

    r=mosquittopp::reinitialise(m_clientId.isEmpty() ? NULL : m_clientId.toLocal8Bit().data(), m_cleanSession);
    if (r!=MOSQ_ERR_SUCCESS) {
        qWarning() << "Failed to set client id";
        m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
        return r;
    }

    if (m_username.isEmpty()==false) {
        r=mosquittopp::username_pw_set(m_username.toLocal8Bit().data(), m_password.isEmpty() ? NULL : m_password.toLocal8Bit().data());
        if (r!=MOSQ_ERR_SUCCESS) {
            qWarning() << "Failed to set username/password";
            m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
            return r;
        }
    }


    if (m_tlsEnabled) {
        tls_set(m_tls_cafile.toLatin1().data(), m_tls_capath.toLatin1().data(), m_tls_cert.toLatin1().data(), m_tls_key.toLatin1().data(), NULL);
        tls_insecure_set(m_tls_insecure);
    }

    r=mosquittopp::connect_async(m_hostname.toLocal8Bit().data(), m_port, m_keepalive);
    if (r!=MOSQ_ERR_SUCCESS) {
        qWarning() << "Connection failure" << r;
        m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
        return r;
    }

    if (connectSocketNotifiers()==false) {
        return MOSQ_ERR_NOT_FOUND;
    }

    emit connecting();

    return r;
}

int QMosquitto::reconnectToHost()
{
    int r;

    r=reconnect_async();

    shutdown();

    if (r!=MOSQ_ERR_SUCCESS) {
        qWarning() << "Connection failure";
        m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
        return r;
    }

    if (connectSocketNotifiers()==false) {
        return MOSQ_ERR_NOT_FOUND;
    }

    emit connecting();

    return r;
}

void QMosquitto::timerEvent(QTimerEvent *event)
{
    int r;

    Q_UNUSED(event)

    r=loop_misc();
    switch (r) {
    case MOSQ_ERR_SUCCESS:

        break;
    default:
        qWarning() << "Misc fail " << r << "MOSQ_ERR_NO_CONN(4)";
        m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
        return;
    }

    if (want_write()==true) {
        // qDebug("NWWW");
        // loopWrite();
        m_notifier_write->setEnabled(true);
    }
}

void QMosquitto::loopRead()
{
    int r;

    // qDebug("LR");
    m_notifier_write->setEnabled(true);
    r=loop_read();
    if (r!=MOSQ_ERR_SUCCESS) {
        qWarning() << "Read fail " << r << "MOSQ_ERR_TLS(8)" << isConnected();//MOSQ_ERR_CONN_LOST
        m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
    }
}

void QMosquitto::loopWrite()
{
    int r;

    m_notifier_write->setEnabled(false);

    r=loop_misc();
    if (r!=MOSQ_ERR_SUCCESS) {
        qWarning() << "Misc fail " << r << "MOSQ_ERR_NO_CONN(4)";
        m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
    }

    if (want_write()==false) {
        return;
    }

    // qDebug("LW");
    r=loop_write();
    if (r!=MOSQ_ERR_SUCCESS) {
        qWarning() << "Write fail " << r;
        m_lastErrorString = QString("%1: %2").arg(__FUNCTION__).arg(mosq_error_string(r)); emit error();
    }

}

void QMosquitto::shutdown()
{
    if (m_timer>0) {
        killTimer(m_timer);
        m_timer=0;
    }

    if (m_notifier_read) {
        delete m_notifier_read;
        m_notifier_read=NULL;
    }
    if (m_notifier_write) {
        delete m_notifier_write;
        m_notifier_write=NULL;
    }

}

QString QMosquitto::mosq_error_string(int code)
{
    switch(code) {
    case MOSQ_ERR_AUTH_CONTINUE: return "MOSQ_ERR_AUTH_CONTINUE";//: return "";// = -4,
    case MOSQ_ERR_NO_SUBSCRIBERS: return "MOSQ_ERR_NO_SUBSCRIBERS";// = -3,
    case MOSQ_ERR_SUB_EXISTS: return "MOSQ_ERR_SUB_EXISTS";// = -2,
    case MOSQ_ERR_CONN_PENDING: return "MOSQ_ERR_CONN_PENDING";// = -1,
    case MOSQ_ERR_SUCCESS: return "MOSQ_ERR_SUCCESS";// = 0,
    case MOSQ_ERR_NOMEM: return "MOSQ_ERR_NOMEM";// = 1,
    case MOSQ_ERR_PROTOCOL: return "MOSQ_ERR_PROTOCOL";// = 2,
    case MOSQ_ERR_INVAL: return "MOSQ_ERR_INVAL";// = 3,
    case MOSQ_ERR_NO_CONN: return "MOSQ_ERR_NO_CONN";// = 4,
    case MOSQ_ERR_CONN_REFUSED: return "MOSQ_ERR_CONN_REFUSED";// = 5,
    case MOSQ_ERR_NOT_FOUND: return "MOSQ_ERR_NOT_FOUND";// = 6,
    case MOSQ_ERR_CONN_LOST: return "MOSQ_ERR_CONN_LOST";// = 7,
    case MOSQ_ERR_TLS: return "MOSQ_ERR_TLS";// = 8,
    case MOSQ_ERR_PAYLOAD_SIZE: return "MOSQ_ERR_PAYLOAD_SIZE";// = 9,
    case MOSQ_ERR_NOT_SUPPORTED: return "MOSQ_ERR_NOT_SUPPORTED";// = 10,
    case MOSQ_ERR_AUTH: return "MOSQ_ERR_AUTH";// = 11,
    case MOSQ_ERR_ACL_DENIED: return "MOSQ_ERR_ACL_DENIED";// = 12,
    case MOSQ_ERR_UNKNOWN: return "MOSQ_ERR_UNKNOWN";// = 13,
    case MOSQ_ERR_ERRNO: return "MOSQ_ERR_ERRNO";// = 14,
    case MOSQ_ERR_EAI: return "MOSQ_ERR_EAI";// = 15,
    case MOSQ_ERR_PROXY: return "MOSQ_ERR_PROXY";// = 16,
    case MOSQ_ERR_PLUGIN_DEFER: return "MOSQ_ERR_PLUGIN_DEFER";// = 17,
    case MOSQ_ERR_MALFORMED_UTF8: return "MOSQ_ERR_MALFORMED_UTF8";// = 18,
    case MOSQ_ERR_KEEPALIVE: return "MOSQ_ERR_KEEPALIVE";// = 19,
    case MOSQ_ERR_LOOKUP: return "MOSQ_ERR_LOOKUP";// = 20,
    case MOSQ_ERR_MALFORMED_PACKET: return "MOSQ_ERR_MALFORMED_PACKET";// = 21,
    case MOSQ_ERR_DUPLICATE_PROPERTY: return "MOSQ_ERR_DUPLICATE_PROPERTY";// = 22,
    case MOSQ_ERR_TLS_HANDSHAKE: return "MOSQ_ERR_TLS_HANDSHAKE";// = 23,
    case MOSQ_ERR_QOS_NOT_SUPPORTED: return "MOSQ_ERR_QOS_NOT_SUPPORTED";// = 24,
    case MOSQ_ERR_OVERSIZE_PACKET: return "MOSQ_ERR_OVERSIZE_PACKET";// = 25,
    case MOSQ_ERR_OCSP: return "MOSQ_ERR_OCSP";// = 26,
    }
    return "";
}

int QMosquitto::disconnectFromHost()
{
    if (m_isConnected==false)
        return MOSQ_ERR_NO_CONN;
    int r=mosquittopp::disconnect();

    return r;
}

int QMosquitto::subscribe(const QString topic, int qos)
{
    m_notifier_write->setEnabled(true);
    int r=mosquittopp::subscribe(&m_smid, topic.toLocal8Bit().data(), qos);

    return r;
}

int QMosquitto::unsubscribe(const QString topic)
{
    m_notifier_write->setEnabled(true);
    int r=mosquittopp::unsubscribe(&m_mid, topic.toLocal8Bit().data());

    return r;
}

int QMosquitto::publish(const QString topic, QString data, int qos, bool retain)
{
    m_notifier_write->setEnabled(true);
    int r=mosquittopp::publish(&m_pmid, topic.toLocal8Bit().data(), data.size(), data.toLocal8Bit().data(), qos, retain);

    return r;
}

int QMosquitto::publish(const QString topic, QByteArray data, int qos, bool retain)
{
    m_notifier_write->setEnabled(true);
    int r=mosquittopp::publish(&m_pmid, topic.toLocal8Bit().data(), data.size(), data.data(), qos, retain);

    return r;
}

int QMosquitto::setWill(const QString topic, QString data, int qos, bool retain)
{
    m_notifier_write->setEnabled(true);
    int r=will_set(topic.toLocal8Bit().data(), data.size(), data.toLocal8Bit().data(), qos, retain);

    return r;
}

void QMosquitto::clearWill()
{
    m_notifier_write->setEnabled(true);
    will_clear();
}

void QMosquitto::on_connect(int rc)
{
    Q_UNUSED(rc)

    qDebug() << __LINE__;
    m_isConnected=true;
    emit connected();
    emit isConnectedeChanged(m_isConnected);
    m_timer=startTimer(1000);
}

void QMosquitto::on_disconnect(int rc)
{
    Q_UNUSED(rc)

    m_isConnected=false;

    shutdown();

    emit disconnected();
    emit isConnectedeChanged(m_isConnected);
}

void QMosquitto::on_message(const mosquitto_message *message)
{
    QString topic(message->topic);
    QString data=QString::fromLocal8Bit((char *)message->payload, message->payloadlen);

    emit msg(topic, data);

    if (m_topics.contains(topic))
        emit topicMatch(m_topics.value(topic));
}

void QMosquitto::on_error()
{
    qWarning("on_error");
    emit error();
}

void QMosquitto::on_log(int level, const char *str)
{
    // qDebug() << "mqtt: " << level << str;
}

void QMosquitto::setClientId(QString clientId)
{
    if (m_clientId == clientId)
        return;

    m_clientId = clientId;
    emit clientIdChanged(clientId);
}

void QMosquitto::setCleanSession(bool cleanSession)
{
    if (m_cleanSession == cleanSession)
        return;

    m_cleanSession = cleanSession;
    emit cleanSessionChanged(cleanSession);
}

void QMosquitto::setKeepalive(int keepalive)
{
    if (m_keepalive == keepalive)
        return;

    m_keepalive = keepalive;
    emit keepaliveChanged(keepalive);
}

void QMosquitto::setHostname(QString hostname)
{
    if (m_hostname == hostname)
        return;

    m_hostname = hostname;
    emit hostnameChanged(hostname);
}

void QMosquitto::setPort(int port)
{
    if (m_port == port)
        return;

    m_port = port;
    emit portChanged(port);
}

void QMosquitto::setTlsEnabled(bool tlsEnabled)
{
    if (m_tlsEnabled == tlsEnabled)
        return;

    m_tlsEnabled = tlsEnabled;
    emit tlsEnabledChanged(tlsEnabled);
}


void QMosquitto::setUsername(QString username)
{
    if (m_username == username)
        return;

    m_username = username;
    emit usernameChanged(username);
}

void QMosquitto::setPassword(QString password)
{
    if (m_password == password)
        return;

    m_password = password;
    emit passwordChanged(password);
}

QString QMosquitto::lastErrorString()
{
    return m_lastErrorString;
}
